{- This file is part of funbot.
 -
 - Written in 2015 by fr33domlover <fr33domlover@rel4tion.org>.
 -
 - ♡ Copying is an act of love. Please copy, reuse and share.
 -
 - The author(s) have dedicated all copyright and related and neighboring
 - rights to this software to the public domain worldwide. This software is
 - distributed without any warranty.
 -
 - You should have received a copy of the CC0 Public Domain Dedication along
 - with this software. If not, see
 - <http://creativecommons.org/publicdomain/zero/1.0/>.
 -}

module FunBot.Types
    ( Filter (..)
    , BranchFilter
    , PushAnnSpec (..)
    , NewsItemFields (..)
    , NewsAnnSpec (..)
    , NewsFeed (..)
    , BotEnv (..)
    , Settings (..)
    , Shortcut (..)
    , SettingsOption
    , SettingsTree
    , Memo (..)
    , HistoryDisplay (..)
    , UserOptions (..)
    , BotState (..)
    , BotSession
    , ExtEventSource
    , ExtEventHandler
    )
where

import Data.HashMap.Lazy (HashMap)
import Data.HashSet (HashSet)
import Data.Settings.Types (Section (..), Option (..))
import Data.Time.Clock (UTCTime)
import FunBot.ExtEvents (ExtEvent)
import Network.IRC.Fun.Bot.Types (Session, EventSource, EventHandler)
import Web.Feed.Collect (CommandQueue)

-- | Generic item filter
data Filter a = Accept [a] | Reject [a]

-- | Chooser for repo branches whose commits should be announced to IRC
type BranchFilter = Filter String

-- | Configuration for announcing a git repo's commits to a specific channel
data PushAnnSpec = PushAnnSpec
    { -- | IRC channel into which to announce
      pAnnChannel    :: String
      -- Branch filter to choose which branches to announce
    , pAnnBranches   :: BranchFilter
      -- Whether to report all commits in a push ('True') or shorten long
      -- pushes to avoid channel spam ('False').
    , pAnnAllCommits :: Bool
    }

-- | Pick news item fields to display
data NewsItemFields = NewsItemFields
    { dispFeedTitle :: Bool
    , dispAuthor    :: Bool
    , dispUrl       :: Bool
    }

-- | Configuration for announcing news items
data NewsAnnSpec = NewsAnnSpec
    { -- | IRC channels into which to announce
      nAnnChannels :: [String]
      -- | Filter for picking news item fields to display or hide
    , nAnnFields   :: NewsItemFields
    }

-- | A web news feed from which the bot can read and announce new items
data NewsFeed = NewsFeed
    { -- | The feed URL
      nfUrl     :: String
      -- | Whether the feed watcher is watching this feed
    , nfActive  :: Bool
      -- | Item announcement details
    , nfAnnSpec :: NewsAnnSpec
    }

-- | Read-only custom bot environment
data BotEnv = BotEnv
    { -- | Port on which the web hook event source will run
      webHookSourcePort :: Int
      -- | An 'IO' action which schedules saving settings to disk. There is a
      -- wrapper in the 'Session' monad which uses this with the settings
      -- stored in bot state, so you probably don't need this field directly.
    , saveSettings      :: Settings -> IO ()
      -- | Similarly for memos.
    , saveMemos         :: HashMap String [Memo] -> IO ()
      -- | Similarly for user options.
    , saveUserOpts      :: HashMap String UserOptions -> IO ()
      -- | Filename for logging feed listener errors
    , feedErrorLogFile  :: FilePath
      -- | Command queue for controlling the news feed watcher source
    , feedCmdQueue      :: CommandQueue
    }

-- | A special string which the bot can detect and translate into a longer
-- form, e.g. a full URL.
data Shortcut = Shortcut
    { -- | String by which the shortcut is detected. For example, if you'd like
      -- \"SD-258\" to refer to the URL of ticket #258, then you should set
      -- the prefix to \"SD-\".
      shPrefix   :: String
      -- | The generated longer form is a concatenation of this field, the
      -- shortcut string (without the prefix) and 'shAfter'.
    , shBefore   :: String
      -- | The generated longer form is a concatenation of 'shBefore', the
      -- shortcut string (without the prefix) and this field.
    , shAfter    :: String
      -- | The channels in which this shortcut should be applied.
    , shChannels :: [String]
    }

-- | User-modifiable bot behavior settings
data Settings = Settings
    { -- | Maps a Git repo name+owner to annoucement details
      gitAnnChans  :: HashMap (String, String) [PushAnnSpec]
      -- | Maps a feed label to its URL and announcement details
    , watchedFeeds :: HashMap String NewsFeed
      -- | Maps a shortcut label to its spec
    , shortcuts    :: HashMap String Shortcut
    }

-- | Alias for the settings option type
type SettingsOption = Option BotSession

-- | Alias for the settings section type
type SettingsTree = Section BotSession

-- | A message left to an offline user, for them to read later.
data Memo = Memo
    { memoTime    :: String
    , memoSender  :: String
    , memoRecvIn  :: Maybe String
    , memoSendIn  :: Maybe String
    , memoContent :: String
    }

-- | History display options per channel
data HistoryDisplay = HistoryDisplay
    { -- | Whether channel history should be displayed
      hdEnabled  :: Bool
      -- | Maximal number of messages to show
    , hdMaxLines :: Int
    }

-- | Per-user options, consider private user info
data UserOptions = UserOptions
    { -- | History display options per channel
      uoHistoryDisplay :: HashMap String HistoryDisplay
    }

-- | Read-write custom bot state
data BotState = BotState
    { -- | User-modifiable bot behavior settings
      bsSettings    :: Settings
      -- | Settings tree and access definition for UI
    , bsSTree       :: SettingsTree
      -- | Memos waiting for users to connect.
    , bsMemos       :: HashMap String [Memo]
      -- | Per-user options
    , bsUserOptions :: HashMap String UserOptions
    }

-- | Shortcut alias for bot session monad
type BotSession = Session BotEnv BotState

-- | Shortcut alias for event source function type
type ExtEventSource = EventSource BotEnv BotState ExtEvent

-- | Shortcut alias for event handler function type
type ExtEventHandler = EventHandler BotEnv BotState ExtEvent
